﻿#region Copyright information
// 
// Copyright © 2005-2013 Yongkee Cho. All rights reserved.
// 
// This code is a part of the Biological Object Library and governed under the terms of the
// GNU Lesser General  Public License (LGPL) version 2.1 which accompanies this distribution.
// For more information on the LGPL, please visit http://bol.codeplex.com/license.
// 
// - Filename: StudentsTDistribution.cs
// - Author: Yongkee Cho
// - Email: yongkeecho@gmail.com
// - Date Created: 2012-09-06 11:39 AM
// - Last Modified: 2013-01-25 3:59 PM
// 
#endregion
using System;
using System.Collections.Generic;
using BOL.Maths.Functions;

namespace BOL.Maths.Distributions
{
    /// <summary>
    /// Represents a Student t distribution with nu degrees of freedom.
    /// <see>ref/StudentTDistribution</see>
    /// <see>http://en.wikipedia.org/wiki/Student%27s_t-distribution</see>
    /// </summary>
    public sealed class StudentsTDistribution : IUnivariateDistribution<double>, IEquatable<StudentsTDistribution>
    {
        #region Private variables

        private int _nu; // number of degree of freedom

        #endregion

        #region Public properties

        /// <summary>Gets or sets the number of degree of freedom of a Student's t-distribution.</summary>
        public int Nu { get { return _nu; } set { _nu = value; } }

        /// <summary>Gets the range of a Student's t-distribution.</summary>
        public IRange<double> Domain { get { return new Range<double>(double.NegativeInfinity, double.PositiveInfinity); } }

        /// <summary>Gets the mean of a Student's t-distribution.</summary>
        public double Mean
        {
            get
            {
                if (_nu <= 1)
                    throw new Exception("Mean is defined only for nu > 1.");

                return 0; 
            }
        }

        /// <summary>Gets the median of a Student's t-distribution.</summary>
        public double Median { get { return 0; } }

        /// <summary>Gets the mode of a Student's t-distribution.</summary>
        public double Mode { get { return 0; } }

        /// <summary>Gets the variance of a Student's t-distribution.</summary>
        public double Variance
        {
            get
            {
                if (_nu <= 2)
                    throw new Exception("Variance is defined only for nu > 2.");

                return _nu == 2 ? Double.MaxValue : _nu / (double)(_nu - 2);
            }
        }

        /// <summary>Gets the skewness of a Student's t-distribution.</summary>
        public double Skewness
        {
            get
            {
                if (_nu <= 3)
                    throw new Exception("Skewness is defined only for nu > 3.");

                return 0;
            }
        }

        /// <summary>Gets the kurtosis of a Student's t-distribution.</summary>
        public double Kurtosis
        {
            get
            {
                if (_nu <= 4)
                    throw new Exception("Kurtosis is defined only for nu > 4.");

                return 6 / (_nu - 4.0);
            }
        }

        /// <summary>Gets the entropy of a Student's t-distribution.</summary>
        public double Entropy
        {
            get
            {
                var term1 = 0.5 * (_nu + 1);
                var term2 = 0.5 * _nu;

                return term1 * (DistributionFunctions.DiGamma(term1) - DistributionFunctions.DiGamma(term2)) + Math.Log(Math.Sqrt(_nu) * DistributionFunctions.Beta(term2, 0.5));
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Instantiates Student's t-distribution
        /// </summary>
        /// <param name="nu">degree of freedom</param>
        public StudentsTDistribution(int nu)
        {
            if (nu <= 0)
                throw new ArgumentOutOfRangeException("nu", "nu is expected to be positive");

            _nu = nu;
        }

        #endregion

        #region ICloneable implementation

        public StudentsTDistribution Clone()
        {
            return new StudentsTDistribution(_nu);
        }

        object ICloneable.Clone()
        {
            return Clone();
        }
        
        #endregion

        #region Public methods

        /// <summary>
        /// Returns the Student's t-density given value.
        /// </summary>
        /// <param name="value"></param>
        /// <returns>Student's t-density</returns>
        public double Pdf(double value)
        {
            var a = DistributionFunctions.Gamma(_nu + 1 / 2.0);
            var b = DistributionFunctions.Gamma(_nu / 2.0) * Math.Sqrt(_nu * Math.PI);

            return a * Math.Pow(1 + value * value / _nu, -(_nu + 1) / 2.0) / b;
        }

        /// <summary>
        /// Returns the cummulative Student's t-density given value.
        /// </summary>
        /// <param name="value"></param>
        /// <returns>cummulative Student's t-density</returns>
        public double Cdf(double value)
        {
            return DistributionFunctions.IncompleteBeta(_nu / 2.0, _nu / 2.0, value);
        }

        /// <summary>
        /// Returns the value of the Student's t-distribution for given probability.
        /// </summary>
        /// <param name="p">probability</param>
        /// <returns></returns>
        public double Quantile(double p)
        {
            if (p.Equals(0.0))
                return -Double.MaxValue;

            if (p.Equals(1.0))
                return Double.MaxValue;

            if (p.Equals(0.5))
                return 0.0;

            //return IRootFinder.Find(p, 0.0, -0.5 * double.MAX_VALUE, 0.5 * double.MAX_VALUE);
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns a random value from the Student's t-distribution.
        /// <see cref="http://en.wikipedia.org/wiki/Student's_t-distribution#Monte_Carlo_sampling"/>
        /// </summary>
        /// <param name="r"></param>
        /// <returns></returns>
        public double Sample(Random r)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Estimates parameters of the Student's t-distribution using maximum likelihood.
        /// </summary>
        public void MaximumLikelihoodEstimate(IEnumerable<double> source)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Estimates parameters of the Student's t-distribution using Bayesian methods.
        /// </summary>
        public void BayesianEstimate(IEnumerable<double> source, params dynamic[] priorParameters)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IEquatable<StudentsTDistribution> implementation

        public bool Equals(StudentsTDistribution other)
        {
            return _nu.Equals(other._nu);
        }

        #endregion

        #region Object overriden

        public override int GetHashCode()
        {
            return Nu.GetHashCode();
        }

        public override bool Equals(object other)
        {
            if (other == null)
                throw new ArgumentNullException("other");

            if (!(other is StudentsTDistribution))
                throw new InvalidCastException("The 'other' argument is not a StudentsTDistribution object.");
            
            return Equals(other as StudentsTDistribution);
        }

        public override string ToString()
        {
            return String.Format("Student's t(nu = {0})", _nu);
        }

        #endregion
    }
}
